package gf

import (
	"fmt"
	"strings"

	"gitee.com/liumou_site/gbm"
	"github.com/spf13/cast"
)

// Line 读取指定行,首行: 1
// 该方法接收一个整数参数 line，表示目标行号。
// 如果 line 参数小于或等于0，当前行号将被重置为1。
// 如果 line 参数大于文件的实际行数，将设置一个错误并返回。
// 该方法返回 *Text 类型的指针，便于链式调用。
func (rs *Text) Line(line int) *Text {
	// 检查是否存在行错误，如果存在则直接返回实例。
	if rs.LineErr != nil {
		return rs
	}
	// 如果传入的行小于或等于0，那么将重置为1。
	if line <= 0 {
		line = 1
	}
	// 这是程序实际行(从0开始计数)，所以line减1。
	lineTmp := line - 1
	// 对文本进行切割并存储在SliceTmp中。
	SliceTmp := strings.Split(rs.Text, "\n")
	// 移除空的元素，以处理可能存在的空行。
	SliceTmp = gbm.SliceRemoveNull(SliceTmp)
	// 检查目标行号是否超过实际行数，如果超过则设置错误并返回。
	if line > len(SliceTmp) {
		rs.Err = fmt.Errorf("读取行数(%d)大于实际行数(%d)", line, len(SliceTmp))
		rs.LineErr = rs.Err
		return rs
	}

	// 设置当前行为目标行的文本，并返回实例。
	rs.Text = SliceTmp[lineTmp]
	return rs
}

// LineRange 读取指定范围行, 首行: 1
//
// 参数
//
// start: 开始行数（从1开始计数）
// end: 结束行数（包含该行）
// 返回值:
//
// *Text 返回处理后的文本对象
//
// 该方法用于指定要处理的文本行范围，如果指定的行范围有效，则更新文本内容
func (rs *Text) LineRange(start, end int) *Text {
	// 检查是否已有行错误，如果有则直接返回实例
	if rs.LineErr != nil {
		return rs
	}
	// 检查开始行数是否大于或等于结束行数，如果是则报错
	if start >= end {
		rs.Err = fmt.Errorf("开始行数大于或等于结束行数")
		rs.LineErr = rs.Err
		return rs
	}
	// 检查开始行数是否小于1，如果是则报错
	if start < 0 {
		rs.Err = fmt.Errorf("起始行不能小于1")
		rs.LineErr = rs.Err
		return rs
	}
	// 如果起始值为0,则对两个值都进行+1
	if start == 0 {
		start = 1
		end += 1
	}
	// 如果开始和结束相等,则直接取一行
	if start == end {
		rs.Line(start)
		return rs
	}
	// 通过换行符对内容进行切割,得到字符串切片
	txtSlice := strings.Split(rs.Text, "\n")
	// 检查结束行数是否大于内容实际总行数，如果是则报错
	if len(txtSlice) < end {
		rs.Err = fmt.Errorf("结束行数大于内容实际总行数")
		rs.LineErr = rs.Err
		return rs
	}
	// 根据指定的行范围提取文本内容
	res := txtSlice[start-1 : end]
	// 将提取的文本切片转换回字符串，并更新文本内容
	rs.Text = gbm.SliceToString(res, "\n")
	// 重置错误状态
	rs.Err = nil
	rs.LineErr = nil
	return rs
}

// Column 提取文本中的指定列数据
// col 指定要提取的列数，sep 指定列之间的分隔符
// 该方法会根据给定的列数和分隔符对文本进行分割，并提取相应的列数据
func (rs *Text) Column(col int, sep string) *Text {
	// 如果之前的操作中已经设置了ColErr错误，则直接返回
	if rs.ColErr != nil {
		return rs
	}
	// 如果指定的列数小于等于0，则默认提取第一列
	if col <= 0 {
		col = 1
	}
	// 实际程序列数(0起始)
	colTmp := col - 1
	// 通过换行符对内容进行切割,得到字符串切片
	txtSlice := strings.Split(rs.Text, "\n")
	// 对空元素进行去除
	txtSlice = gbm.SliceRemoveNull(txtSlice)
	// 如果切割后的内容为空，则设置错误信息并返回
	if len(txtSlice) == 0 {
		rs.ColErr = fmt.Errorf("数据不足,无法切割")
		rs.Err = rs.ColErr
		return rs
	}
	// 初始化文本内容，用于后续的列数据存储
	rs.Text = ""
	// 遍历每一行数据
	for index, LineData := range txtSlice {
		// 对字符串中的空白符进行移除
		LineSp := strings.Fields(LineData)
		LineData = gbm.SliceToString(LineSp, "")
		// 对行内容进行切割
		SliceTmp := strings.Split(LineData, sep)
		// 对空元素进行去除
		SliceTmp = gbm.SliceRemoveNull(SliceTmp)
		// 如果当前行的列数足够，则提取指定列的数据，否则记录警告日志
		if len(SliceTmp) < col+1 {
			rs.Text = rs.Text + SliceTmp[colTmp] + "\n"
		} else {
			logs.Warn("第" + cast.ToString(index+1) + "行列数不足,跳过内容获取")
			logs.Debug("当前行数据总列数:" + cast.ToString(len(SliceTmp)))
		}
	}
	// 返回处理后的Handle对象
	return rs
}

// ColumnNull 通过切割所有空白符进行列数据截取(\t, \n, \v, \f, \r, ’ ‘, U+0085 (NEL), U+00A0 (NBSP) )
//
// 参数
//
// col: 指定要提取的列数，从1开始计数
//
// 返回值:
//
// *Text 返回处理后的数据对象
func (rs *Text) ColumnNull(col int) *Text {
	// 如果列错误标志已设置，则直接返回
	if rs.ColErr != nil {
		return rs
	}
	// 确保列数至少为1
	if col <= 0 {
		col = 1
	}
	// 计算实际程序列数，从0开始
	colTmp := col - 1
	// 通过换行符对内容进行切割,得到字符串切片
	txtSlice := strings.Split(rs.Text, "\n")
	// 对空元素进行去除
	txtSlice = gbm.SliceRemoveNull(txtSlice)
	// 如果切割后没有数据，设置错误并返回
	if len(txtSlice) == 0 {
		rs.ColErr = fmt.Errorf("数据不足,无法切割")
		rs.Err = rs.ColErr
		return rs
	}
	// 清空文本内容，准备后续处理
	rs.Text = ""
	// 遍历每一行数据
	for index, LineData := range txtSlice {
		// 对字符串中的空白符进行移除
		LineSp := strings.Fields(LineData)
		// 如果当前行的列数不足，则记录警告并跳过
		if len(LineSp) < col+1 {
			rs.Text = rs.Text + LineSp[colTmp] + "\n"
		} else {
			// 记录警告信息
			logs.Warn("第" + cast.ToString(index+1) + "行列数不足,跳过内容获取")
			// 记录调试信息
			logs.Debug("当前行数据总列数:" + cast.ToString(len(LineSp)))
		}
	}
	// 返回处理后的数据对象
	return rs
}

// ColumnRange 截取文本中指定列范围的内容,首列: 1
//
// 参数
//
// start: 开始列数（从1开始计数）
// end: 结束列数（包含该列）
// sep: 列之间的分隔符
//
// 返回
//
// *Text: 返回处理后的文本对象
//
// 该方法首先会根据start和end参数检查截取范围是否有效，如果无效则设置错误信息并返回
// 之后遍历文本的每一行，对每一行按照sep进行分割，并根据start和end参数截取相应的列
// 如果某一行的列数少于end指定的列数，则会记录警告信息并跳过该行
// 最后将截取到的内容重新组合成文本，并更新对象的Text属性
func (rs *Text) ColumnRange(start, end int, sep string) *Text {
	if rs.ColErr != nil {
		return rs
	}
	txtSlice := strings.Split(rs.Text, "\n") // 通过换行符对内容进行切割,得到字符串切片
	if start >= end {
		rs.Err = fmt.Errorf("开始行数大于或等于结束行数")
		return rs
	}
	if len(txtSlice) < end {
		rs.Err = fmt.Errorf("结束行数大于内容实际总行数")
		return rs
	}
	rs.Text = "" // 初始化文本信息
	for index, line := range txtSlice {
		lineSlice := strings.Split(line, sep) // 对每行进行遍历
		if len(lineSlice) < end {
			// 如果本行列数少于结束列则跳过此行
			mess := "第 " + cast.ToString(index) + " 行截取列数小于: " + cast.ToString(end) + " - 本次跳过"
			logs.Warn(mess)
		} else {
			res := lineSlice[start-1 : end-1] // 截取每一行的指定列
			txt := gbm.SliceToString(res, "\n")
			rs.Text = rs.Text + txt + "\n"
		}
	}
	rs.Err = nil
	return rs
}

// Reset 重置 Text 对象的状态，为其成员变量赋予初始值。
// 这个方法主要用于清除任何先前的错误状态，使 Text 对象能够开始新的操作。
// 返回值是 *Text 类型，允许进行方法链式调用。
func (rs *Text) Reset() *Text {
	rs.Err = nil       // 清除一般错误状态
	rs.GrepErr = nil   // 清除与搜索相关的错误状态
	rs.ColErr = nil    // 清除与列操作相关的错误状态
	rs.LineErr = nil   // 清除与行操作相关的错误状态
	rs.Text = rs.texts // 将 Text 字段恢复为初始状态，准备进行新的文本处理操作
	return rs          // 返回重置后的 Text 对象，支持链式调用
}
